// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <atomic>
#include <memory>
#include <thread>

namespace tbir::runtime::internal {
    class IRunnable {
    public:
        inline void Run() noexcept {
            try {
                this->RunImpl();
            } catch (...) {
                e_ = std::current_exception();
            }
            std::atomic_thread_fence(std::memory_order_acquire);
            try {
                this->SetDone();
            } catch (...) {
                if (!e_) {
                    e_ = std::current_exception();
                }
            }
        }

        inline void Wait() {
            while (!Done()) {
                std::this_thread::sleep_for(std::chrono::nanoseconds(1));
            }
            if (e_) {
                std::rethrow_exception(e_);
            }
        }

        virtual bool Done() = 0;

    protected:
        virtual void RunImpl() = 0;

        virtual void SetDone() = 0;

    protected:
        std::exception_ptr e_;
    };

    using IRunnablePtr = std::shared_ptr<IRunnable>;

}  // namespace tbir::runtime::internal
